#pragma region // Header + Documentation...
/*
 * -----------------------------------------------------------------------------
 * Project   : Provide support for OLEDb
 * Purpose   : Declare the Db<X> classe family that implement an
 *           : OLEDb consumer.
 * Author    : Eduardo Sobrino
 * Remarks   : I decided to implement a Db<x> class family using raw OLEDb
 *           : to be used when performance is an issue. The classes here
 *           : implement a similar set of classes as ADO COM, still since we
 *           : are using OLEDb directly we should have better performance.
 *           :
 *           : A secondary benefit is that we are spending time to implement
 *           : database access in raw OLEDb that will help us to understand
 *           : the details and capabilities of this technology and provide
 *           : us with additional functionality and control beyond the use
 *           : of ADO.
 *           :
 *           : About the experience I should tell that since I had implemented
 *           : a similar class family using ODBC (v2.0, v3.5) and had been
 *           : working with raw ODBC for the past 2 years I will say that
 *           : implementing access with OLEDb has been relatively painless.
 *           :
 *           : To help me on working with OLEDb I have used the following
 *           : references:
 *           :
 *           : - Microsoft OLE DB 2.0 Programmer's Reference and Data Access
 *           :   SDK (1998), Microsoft Press
 *           : - MSDB Library October 2000 and January 2000.
 *           : - OLE DB AND ODBC Developer's Guide, Wood Chuck (1999), M&T
 *           :   Books
 *           :
 *           : What has been a bit difficult is handling MS-SQL TEXT fields
 *           : since they are treated as a BLOB and I have not figure out why
 *           : for some reason if they are the only piece of information been
 *           : requested or is the last field in a query the data is fetched
 *           : successfully. if they are not the only field requested and is
 *           : not the last one in the query, the Provider return STATUS=8
 *           : (meaning DBSTATUS_E_UNAVAILABLE). TEXT fields can be handle as
 *           : BYNARY or by using the ISequentialStream, still either will
 *           : behave the same (and as describe above). If you reader can
 *           : provide some insides into this, just tell me please.
 *           :
 *           : Also note that I am declaring my own enumerators and macros to
 *           : support type mappings for later field and parameter bindings.
 *           : This is good in the sence that I may take my ODBC classes and
 *           : provide a Db<x> family that uses the exact properties and
 *           : methods but that access ODBC instead of OLEDb.  I feel that
 *           : since I have work with ADO and ODBC for a while this Db<x>
 *           : class familiy is better design than those that I implemented
 *           : for ODBC and will be nice that I can use the exact classes for
 *           : either.
 *           :
 * Date      : Feb/2k1
 * Update    : Jun/2k7 - Move code to Kif Framework and added replaced banded
 *           :         - functions moving to "secure-string" handling...
 */
#pragma endregion

#pragma once

#pragma region   // Include Files

#include <olEDb.h>
#include <SQLOLEDb.h>

#include <stdio.h>
#include <tchar.h>
#include <stddef.h>
#include <windows.h>
#include <olEDb.h>      // OLE DB include files
#include <olEDberr.h> 

// 06-11-04 Create an instance of the SQL OlEDb provider
#ifdef USE_MSSQL_2000
#include <sqlolEDb.h>
#else
#include <sqlncli.h>    // 06-11-04 Support for MS-SQL Native Client
#endif

#include <msdaguid.h>   // ODBC provider include files
#include <msdasql.h>
#include <cguid.h>

#include "Win32.String.h"
#include "Win32.Diagnostics.Log.h"

#pragma endregion

// -----------------------------------------------------------------------------

namespace Kif
{
namespace Win32
{
namespace Data
{
namespace OleDbClient
{

// -----------------------------------------------------------------------------
#pragma region // Common Enumerators and Macros

#define OLEDB_SUCCESS(x) (x == S_OK)

// ROUNDUP on all platforms pointers must be aligned properly

#define ROUNDUP_AMOUNT        8
#define ROUNDUP_(size,amount) (((ULONG)(size)+((amount)-1))&~((amount)-1))
#define ROUNDUP(size)         ROUNDUP_(size, ROUNDUP_AMOUNT)

// -----------------------------------------------------------------------------
// Defines for conditional compilation ...

// The DB_TEXT_AS_BYTE is used to map a MS-SQL TEXT column as BYTE[8000] else
// it will try using ISequentialStream ... so comment out the macro if you
// prefer to use ISequentialStream approach .

// #define DB_TEXT_AS_BYTE
#define DB_TEXT_MAX_LEN 8000

// -----------------------------------------------------------------------------
// Define supported database types and enumerators ...

#define TDbInt      long
#define TDbDouble   double
#define TDbFloat    float
#define TDbDate     DBDATE
#define TDbTime     DBTIME
#define TDbDatetime DBTIMESTAMP
#define TDbChar     TCHAR

enum DbDataType
{
   EDbUnknown  = -1,

   // 06-11-04 added UNICODE Support
   #ifdef _UNICODE
   EDbChar     = DBTYPE_WSTR,
   EDbVarChar  = DBTYPE_WSTR,
   #else
   EDbChar     = DBTYPE_STR,
   EDbVarChar  = DBTYPE_STR,
   #endif

   EDbVarText  = DBTYPE_BYTES,
   EDbInteger  = DBTYPE_I4,
   EDbDouble   = DBTYPE_R8,
   EDbDatetime = DBTYPE_DBTIMESTAMP,
   EDbText     = DBTYPE_IUNKNOWN
}  ;

enum EDbParamIO {
   EDbNotParam = DBPARAMIO_NOTPARAM,
   EDbInput    = DBPARAMIO_INPUT,
   EDbOutput   = DBPARAMIO_OUTPUT
}  ;

enum EDbResults {
   EDbOk           = S_OK,
   EDbNoResult     = DB_S_NORESULT,
   EDbLimitReached = DB_S_STOPLIMITREACHED
}  ;

enum DbDataSourceObject {
   DbDso_Unknown = 0,
   DbDso_Odbc    = 1,
   DbDso_NativeSqlClient = 2,
   DbDso_Oracle  = 3
}  ;

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Describe / define the DbSettings structure

typedef struct DbSettings_
{
   DbDataSourceObject DataSourceObject;

   TCHAR *pHost;                     // host
   TCHAR *pCatalog;                  // catalog
   TCHAR *pUserId;                   // user id
   TCHAR *pPassword;                 // user password

   // constructure, init variables

   DbSettings_() {
      pHost = pCatalog = pUserId = pPassword = NULL;
      DataSourceObject = DbDso_NativeSqlClient;
   }

   void Free()
   {
      if (pHost != NULL)
         free(pHost);
      if (pCatalog != NULL)
         free(pCatalog);
      if (pUserId != NULL)
         free(pUserId);
      if (pPassword != NULL)
         free(pPassword);

      pHost     = NULL;
      pCatalog  = NULL;
      pUserId   = NULL;
      pPassword = NULL;
   }  // end of Free()

  ~DbSettings_()
   {
      //Free();
   }

   void Set(TCHAR *pNewHost,   TCHAR *pNewCatalog,
            TCHAR *pNewUserId, TCHAR *pNewPassword)
   {
      Free();

      if (pNewHost)
         pHost = _tcsdup(pNewHost);
      if (pNewCatalog)
         pCatalog = _tcsdup(pNewCatalog);
      if (pNewUserId)
         pUserId = _tcsdup(pNewUserId);
      if (pNewPassword)
         pPassword = _tcsdup(pNewPassword);
   }  // end of Set...

}  DbSettings;

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Describe / define the DbValidate

const int cUndefinedYear = 1800;

class DbValidate
{
   private:
   public:

      static bool DateTime(TDbDatetime *pDate)
      {

         // make sure that month is ok

         if (pDate->month <= 0)
            pDate->month = 1;
         else
         if (pDate->month > 12)
            pDate->month = 12;

         // make sure that day is ok

         if (pDate->day <= 0)
            pDate->day = 1;
         else
         if (pDate->day >= 31) {
            if (pDate->month == 2)
               pDate->day = 28;
            else
               pDate->day = 30;
         }

         // make sure that year is ok

         if (pDate->year <= 0)
            pDate->year = cUndefinedYear;

          return(true);
      }  // end of validate date time
}  ;  // end of DbValidate

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Describe / define the DbField, DbFields and DbParam

class DbField {
   private:
   public:

      DbDataType eType;              // param type
      EDbParamIO eParamIO;           // parameter in or out
      UINT       iSize;              // integer part
      UINT       iDec;               // decimal places
      ULONG      iSizeInBytes;       // size in bytes
      ULONG      iOffset;            // buffer offset

      // provide pointers for any kind of data that we will support

      union {
         void         *pVoid;        // any pointer
         char         *pByte;        // byte value
         TDbChar      *pChar;        // string value
         TDbInt       *pInt;         // long value
         TDbDouble    *pDouble;      // float value
         TDbFloat     *pFloat;       // real value
         TDbDate      *pDate;        // date value
         TDbTime      *pTime;        // time value
         TDbDatetime  *pDatetime;    // datetime value
      }  vValue;

      DbField()
      {
         eType  = EDbUnknown;
         iSize  = 0;
         iDec   = 0;
         vValue.pVoid = NULL;
         iSizeInBytes = 0;
         eParamIO = EDbNotParam;
         iOffset  = 0;
      }

      inline void Set(void *vval,DbDataType etype,int isize,int idec,long lbytes)
      {
         eType = etype;
         iSize = isize;
         iDec  = idec;
         vValue.pVoid = vval;
         iSizeInBytes = lbytes;
      }  // end of value ...

      inline void Set(TCHAR *pStr,int isize)
      {
         eType = EDbChar;
         iSize = isize;
         vValue.pChar = pStr;
         iSizeInBytes = sizeof(TCHAR)*(isize+1);
      }

      inline void Set(double *pDouble)
      {
         eType = EDbDouble;
         iSize = 15;
         iDec  = 2;
         vValue.pDouble = pDouble;
         iSizeInBytes = sizeof(double);
      }

      inline void Set(long *pLong)
      {
         eType = EDbInteger;
         iSize = 15;
         vValue.pInt = pLong;
         iSizeInBytes = sizeof(long);
      }

      inline void Set(TDbDatetime *pDatetime)
      {
         eType = EDbDatetime;
         iSize = sizeof(DBTIMESTAMP);
         vValue.pDatetime = pDatetime;
         iSizeInBytes = sizeof(DBTIMESTAMP);
      }

}  ;  // end of DbField

inline bool IsBlobField(DBBINDING *pField)
   { return(((pField)->wType == DBTYPE_IUNKNOWN) ||
            ((pField)->dwFlags & DBCOLUMNFLAGS_ISLONG)); };

#define _CHAR char

// 06-11-04 added string handling support to replace some memset, memcpy...
#define _CharClearString(string, strLength) \
   {ULONG i;for(i=0;i<strLength;i++) *(string+i)=NULL;}
#define _CharCopyString(outString,outLength,inString,inLength) \
   {ULONG i,c; c=(inLength>outLength)?outLength:inLength; \
    for(i=0;i<c;i++) *(outString+i)=*(inString+i); *(outString+c)=NULL; }

class DbConnection;
class DbFields
{
   private:
      DbConnection *pParentObj;      // parent object that created this

   public:

      UINT     iCount;               // number of allocated fields
      DbField *pList;                // allocated number of fields
      _CHAR   *pRowBuffer;           // pointer to values ...
      ULONG    iRowLen;

      DbFields(int nFields)
      {
         pList  = new DbField[nFields];
         iCount = (pList) ? nFields : 0;
         pParentObj = NULL;
         pRowBuffer = NULL;
         iRowLen    = 0;
      }  ;

     ~DbFields() { Free(); }

      void Free() {
         if (pList)
            delete pList;
         pList = NULL;
         if (pRowBuffer)
            delete pRowBuffer;
         pRowBuffer = NULL;
      }  // end of free allocated resources

      void Set(_CHAR *pBuff) { pRowBuffer = pBuff; }
      void Set(DbConnection *pConn) { pParentObj = pConn; }

      inline ULONG BindFields(DBBINDING *pDbBindings);
      inline ULONG BindParams(DBPARAMBINDINFO *pDbBindings);

      inline void Set(int iField,void *vval,DbDataType etype,int isize,
         int idec,long lbytes) {
         pList[iField].eType = etype;
         pList[iField].iSize = isize;
         pList[iField].iDec  = idec;
         pList[iField].vValue.pVoid = vval;
         pList[iField].iSizeInBytes = lbytes; 
      }  // end of value ...

      inline void Set(int iField,TCHAR *pStr,int isize) {
         pList[iField].eType = EDbChar;
         pList[iField].iSize = isize;
         pList[iField].vValue.pChar = pStr;
         pList[iField].iSizeInBytes = sizeof(TCHAR)*(isize+1);
      }

      inline void SetText(int iField,TCHAR *pStr,int isize) {
         pList[iField].eType = EDbText;
         pList[iField].iSize = isize;
         pList[iField].vValue.pChar = pStr;
         pList[iField].iSizeInBytes = sizeof(TCHAR)*(isize+1);
      }

      inline void Set(int iField,double *pDouble) {
         pList[iField].eType = EDbDouble;
         pList[iField].iSize = 15;
         pList[iField].iDec  = 2;
         pList[iField].vValue.pDouble = pDouble;
         pList[iField].iSizeInBytes = sizeof(double);
      }

      inline void Set(int iField,long *pLong) {
         pList[iField].eType = EDbInteger;
         pList[iField].iSize = 15;
         pList[iField].vValue.pInt = pLong;
         pList[iField].iSizeInBytes = sizeof(long);
      }

      inline void Set(int iField,TDbDatetime *pDatetime) {
         pList[iField].eType = EDbDatetime;
         pList[iField].iSize = sizeof(DBTIMESTAMP);
         pList[iField].vValue.pDatetime = pDatetime;
         pList[iField].iSizeInBytes = sizeof(DBTIMESTAMP);
      }

      inline void MapChar(int iField,ULONG iOffset,int isize) {
         pList[iField].eType = EDbChar;
         pList[iField].iSize = isize;
         pList[iField].vValue.pChar = (TCHAR*)pRowBuffer+iOffset;
         pList[iField].iSizeInBytes = sizeof(TCHAR)*(isize+1);
         pList[iField].iOffset = iOffset;
      }

      inline void MapText(int iField,ULONG iOffset,int isize) {
         pList[iField].eType = EDbText;
         pList[iField].iSize = isize;
         pList[iField].vValue.pChar = (TCHAR*)pRowBuffer+iOffset;
         pList[iField].iSizeInBytes = sizeof(TCHAR)*(isize+1);
         pList[iField].iOffset = iOffset;
      }

      inline void MapDouble(int iField,ULONG iOffset) {
         pList[iField].eType = EDbDouble;
         pList[iField].iSize = 15;
         pList[iField].iDec  = 2;
         pList[iField].vValue.pChar = (TCHAR*)pRowBuffer+iOffset;
         pList[iField].iSizeInBytes = sizeof(double);
         pList[iField].iOffset = iOffset;
      }

      inline void MapLong(int iField,ULONG iOffset) {
         pList[iField].eType = EDbInteger;
         pList[iField].iSize = 15;
         pList[iField].vValue.pChar = (TCHAR*)pRowBuffer+iOffset;
         pList[iField].iSizeInBytes = sizeof(long);
         pList[iField].iOffset = iOffset;
      }

      inline void MapDateTime(int iField,ULONG iOffset) {
         pList[iField].eType = EDbDatetime;
         pList[iField].iSize = sizeof(DBTIMESTAMP);
         pList[iField].vValue.pChar = (TCHAR*)pRowBuffer+iOffset;
         pList[iField].iSizeInBytes = sizeof(DBTIMESTAMP);
         pList[iField].iOffset = iOffset;
      }
}  ;

class DbParams : public DbFields
{
   private:
   public:

      bool              bWillRepeat;      // 'true' to use more than once

      HRESULT           hrLastError;      // last result error handle

      HACCESSOR         hAccessor;        // pointer to params accessor
      IAccessor        *pIAccessor;       // pointer to accessor object  
      DB_UPARAMS       *pOrdinals;        // parameters ordinals
      DBPARAMBINDINFO  *pParamBindings;   // parameters bindindings
      DBBINDING        *pFieldBindings;   // parameters field bindings
      DBBINDSTATUS     *pStatus;          // for individual binding validity
      DBPARAMS         *pParams;
      _CHAR            *pValues;          // pointer to value structure
      ULONG             ulParamRecLen;    // length of params field record len

      inline void Init()
      {
         hrLastError       = 0;
         hAccessor         = NULL;
         pIAccessor        = NULL;  
         pParams           = NULL;
         pOrdinals         = NULL;
         pParamBindings    = NULL;
         pFieldBindings    = NULL;
         pStatus           = NULL;
         pValues           = NULL;
         ulParamRecLen     = 0;
      }  ;

      void Free();

      DbParams(int nParams);
     ~DbParams();

      inline ULONG BindParams() {
         return (DbFields::BindParams(pParamBindings));
      }  // end of bind parameters ...

      inline void SetIO(int iField,EDbParamIO eParamIO) {
         pList[iField].eParamIO = eParamIO;
      }  // end of value parameter is for in or out ...

      inline void IsForOutput(int iField) {
         pList[iField].eParamIO = EDbOutput;
      }  // end of value parameter is for out ...

      inline void IsForInput(int iField) {
         pList[iField].eParamIO = EDbInput;
      }  // end of value parameter is for input ...

      bool Prepare(ICommandText *pICommandText);

      // calculate field address using parameter bindings ...

      _CHAR *GetAddress(int iParamIndex) {
         return(pValues+(pFieldBindings+iParamIndex)->obValue);
      }

      // inline's for setting parameter values

      inline int SetString(int iParamIndex, TCHAR *pVal,int iValLen)
      {
         int len = pFieldBindings[iParamIndex].cbMaxLen < ((UINT)iValLen) ?
            pFieldBindings[iParamIndex].cbMaxLen : iValLen;
         TCHAR *pData = (TCHAR*)GetAddress(iParamIndex);
         Kif::Win32::String::Copy(pData,len,pVal);
         return(len);
      }

      inline void SetLong(int iParamIndex,long lVal) {
         *((long*)GetAddress(iParamIndex)) = lVal;
      }
      inline void SetInt(int iParamIndex,int iVal) {
         *((int*)GetAddress(iParamIndex)) = iVal;
      }
      inline void SetFloat(int iParamIndex,float fVal) {
         *((float*)GetAddress(iParamIndex)) = fVal;
      }
      inline void SetDouble(int iParamIndex,double dVal) {
         *((double*)GetAddress(iParamIndex)) = dVal;
      }

      inline void SetDateTime(int iParamIndex,TDbDatetime *pDatetime)
      {
         DBTIMESTAMP *pDTime = (DBTIMESTAMP*)GetAddress(iParamIndex);
         *pDTime = *pDatetime;
      }

      inline void SetValues()
      {
         ULONG count;
         for (count = 0; count < iCount; count++)
         {
            if (pList[count].eParamIO == DBPARAMIO_INPUT)
            {
               switch (pList[count].eType)
               {
                  case EDbChar:
                     SetString(count,pList[count].vValue.pChar,
                        pList[count].iSize);
                     break;
                  case EDbInteger:
                     SetLong(count,*pList[count].vValue.pInt);
                     break;
                  case EDbDouble:
                     SetDouble(count,*pList[count].vValue.pDouble);
                     break;
                  case EDbDatetime:
                     SetDateTime(count,pList[count].vValue.pDatetime);
                     break;
               }
            }
         }
      }  // end of SetValues

      // inline's for getting fetched parameter values ...

      inline int GetString(int iParamIndex, TCHAR *pVal,int iValLen)
      {
         int len = pFieldBindings[iParamIndex].cbMaxLen < ((UINT)iValLen) ?
            pFieldBindings[iParamIndex].cbMaxLen : iValLen;
         TCHAR *pData = (TCHAR*)GetAddress(iParamIndex);
         Kif::Win32::String::Copy(pVal,len,pData);
         *(pVal+len) = NULL;
         return(len);
      }

      inline void GetLong(int iParamIndex,long *lVal) {
         *lVal = *((long*)GetAddress(iParamIndex));
      }
      inline void GetInt(int iParamIndex,int *iVal) {
         *iVal = *((int*)GetAddress(iParamIndex));
      }
      inline void GetFloat(int iParamIndex,float *fVal) {
         *fVal = *((float*)GetAddress(iParamIndex));
      }
      inline void GetDouble(int iParamIndex,double *dVal) {
         *dVal = *((double*)GetAddress(iParamIndex));
      }
      inline void GetDateTime(int iParamIndex,TDbDatetime *pDatetime) {
         DBTIMESTAMP *pDTime = (DBTIMESTAMP*)GetAddress(iParamIndex);
         *pDatetime = *pDTime;
      }

      inline void GetValues() {
         ULONG count;
         for (count = 0; count < iCount; count++) {
            if (pList[count].eParamIO == DBPARAMIO_OUTPUT) {
               switch (pList[count].eType) {
                  case EDbChar:
                     GetString(count,pList[count].vValue.pChar,
                        pList[count].iSize);
                     break;
                  case EDbInteger:
                     GetLong(count,pList[count].vValue.pInt);
                     break;
                  case EDbDouble:
                     GetDouble(count,pList[count].vValue.pDouble);
                     break;
                  case EDbDatetime:
                     GetDateTime(count,pList[count].vValue.pDatetime);
                     break;
               }
            }
         }
      }

}  ;  // end of DbParams

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Describe / define the DbConnection

class DbFields;
class DbParams;
class DbResult;
class DbFastLoad;

class DbConnection
{
   private:

      // OLEDb 

      IDBCreateSession *pIDbCreateSession;   // current create session object
      IDBCreateCommand *pIDbCreateCommand;   // current create command object
      ICommand         *pICommand;           // current command object

      IDBInitialize    *pIDBInitialize;
      bool              bUseODBC;            // use olEDb odbc provider

      bool InitDSO(TCHAR *pDSN,TCHAR *pCatalog,TCHAR *pUserId,TCHAR *pPasswd,
         DbDataSourceObject Dso);
      bool InitDSO(TCHAR *pDSN,TCHAR *pCatalog,TCHAR *pUserId,TCHAR *pPasswd)
      {
         return(InitDSO(pDSN,pCatalog,pUserId,pPasswd,DbDso_NativeSqlClient));
      }

      bool SetInitProps(TCHAR *pDSN,TCHAR *pCatalog,TCHAR *pUserId,TCHAR *pPasswd);

      bool SetCommandProperties(ICommandText *pICommandText);
      inline ICommandText *GetICommandText();

   public:

      IMalloc       *g_pIMalloc;         // global task memory allocator
      HRESULT        hrLastHR;           // contains the last HRESULT

      DbConnection();
     ~DbConnection();

      // provide for fast load (access to the 'IRowsetFastLoad' ...)

      HRESULT SetFastLoadProperty(bool fSet);

      // use ODBC provider

      void SetODBCProvider(bool useProv) { bUseODBC = useProv; }

      // connect / disconnect to / from a known ODBC data source ...

      bool Connect(TCHAR *pDSN,TCHAR *pUserId,TCHAR *pPasswd)
      {  if (InitDSO(pDSN,NULL,pUserId,pPasswd))
            return(true);
         return(false);
      }

      bool Connect(TCHAR *pDSN,TCHAR *pCatalog,TCHAR *pUserId,TCHAR *pPasswd)
      {  if (InitDSO(pDSN,pCatalog,pUserId,pPasswd))
            return(true);
         return(false);
      }

      bool Connect(DbSettings &settings)
      {  if (InitDSO(settings.pHost,settings.pCatalog,settings.pUserId,
            settings.pPassword,settings.DataSourceObject))
            return(true);
         return(false);
      }

      bool Disconnect();

      // execute procedure

      DbResult *PrepareProcedure(
         TCHAR *pProcName, DbFields *pColumns, DbParams *pParams);
      DbResult *PrepareProcedure(TCHAR *pProcName, DbParams *pParams)
      {  return(PrepareProcedure(pProcName,NULL,pParams)); }

      bool ExecuteProcedure(TCHAR *pProcName);

      // provide to allocate parameters and fields ...

      DbFields *AllocFields(int nFields)
         { DbFields *pFields = new DbFields(nFields);
           if (pFields) { pFields->Set(this); }
           return(pFields); }
      DbParams *AllocParams(int nParams)
         { DbParams *pParams = new DbParams(nParams);
           if (pParams) { pParams->DbFields::Set(this);
              int count;
              for (count=0; count < nParams; count++)
                 pParams->pList[count].eParamIO = EDbInput; }
           return(pParams); }

      // execute given query

      ULONG BindField(DBBINDING *pField);

      DbFastLoad *PrepareForFastLoad(
         TCHAR *pTableName, DbFields *pFields);

      DbResult *Prepare(TCHAR *pQueryString,
         DbFields *pFields,DbParams *pParams,bool bToRepeat);

      inline DbResult *Prepare(TCHAR *pQueryString,DbFields *pFields,
         DbParams *pParams) {
         return(Prepare(pQueryString,pFields,pParams,false));
      }  ;

      inline DbResult *Prepare(TCHAR *pQueryString,DbFields *pFields) {
         return(Prepare(pQueryString,pFields,NULL,false));
      }  ;

      inline DbResult *Prepare(TCHAR *pQueryString,DbParams *pParams) {
         return(Prepare(pQueryString,NULL,pParams,false));
      }  ;

      inline DbResult *Prepare(TCHAR *pQueryString) {
         return(Prepare(pQueryString,NULL,NULL,false));
      }  ;

      inline DbResult *PrepareToRepeat(TCHAR *pQueryString,
         DbFields *pFields, DbParams *pParams) {
         return(Prepare(pQueryString,pFields,pParams,true));
      }  ;

      inline DbResult *PrepareToRepeat(
         TCHAR *pQueryString,DbFields *pFields) {
         return(Prepare(pQueryString,pFields,NULL,true));
      }  ;

      inline DbResult *PrepareToRepeat(
         TCHAR *pQueryString,DbParams *pParams) {
         return(Prepare(pQueryString,NULL,pParams,true));
      }  ;

      inline DbResult *PrepareToRepeat(TCHAR *pQueryString) {
         return(Prepare(pQueryString,NULL,NULL,true));
      }  ;

      // handle errors

      bool CheckError(HRESULT hrLastErr);
}  ;  // end of DbConnection

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Describe / define the DbCommand

class DbCommand
{
   private:

      DbConnection *pConnection;      // pointer to active connection
      TCHAR *pCommandText;              // command text

   public:

      bool SetActiveConnection(DbConnection *pConn) {
         pConnection = pConn;
      }

      bool SetCommandText(TCHAR *pCommText) {
         if (pCommandText)
            free(pCommandText);
         pCommandText = Kif::Win32::String::duplicate(pCommText);
      }  // end of set command text
}  ;  // end of DbCommand

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Describe / define the DbColumnInfo

#define IsValidColumn(x) ((x) >=0) && ((x) <= nCols)

class DbColumnInfo
{
   public:

      bool     bSuccess;
      ULONG    ulLength;
      DBSTATUS dbStatus;
      _CHAR    *pVal;

      bool bIsFixedLength;
      bool bIsNullable;
      bool bIsLong;
      bool bIsNull;
      bool bIsTruncated;

      // get length, status and value of current column ...

      DbColumnInfo(_CHAR *pRow,DBBINDING *pBinding,DBCOLUMNINFO *pColInfo)
      {
         pVal = pRow+pBinding->obValue;

         // we have may not fetch the status field !

         if (pBinding->dwPart | DBPART_STATUS)
         {
            memcpy_s((_CHAR*)&dbStatus,sizeof(DBSTATUS),
               pRow+pBinding->obStatus,sizeof(DBSTATUS));
            switch (dbStatus)
            {
               case DBSTATUS_S_OK:
                  bIsNull  = false;
                  bIsTruncated = false;
                  bSuccess = true;
                  break;
	            case DBSTATUS_S_ISNULL:
                  bIsNull  = true;
                  bIsTruncated = false;
                  bSuccess = true;
                  break;
	            case DBSTATUS_S_TRUNCATED:
                  bIsNull  = false;
                  bIsTruncated = true;
                  bSuccess = true;
                  break;
               case DBSTATUS_E_BADACCESSOR:
               case DBSTATUS_E_UNAVAILABLE:
                  bSuccess = false;
                  break;
               default:
                  bSuccess = false;
            }
         }

         // ELSE
         // we don't have access to the column result status therefore assume
         // all is ok ...

         else
            bSuccess = true;

         // check column flags ...

         if (pColInfo)
         {
            bIsFixedLength = (pColInfo->dwFlags &
                              DBCOLUMNFLAGS_ISFIXEDLENGTH) != 0;
            bIsNullable    = (pColInfo->dwFlags &
                              DBCOLUMNFLAGS_ISNULLABLE) != 0;
            bIsLong        = (pColInfo->dwFlags & DBCOLUMNFLAGS_ISLONG) != 0;
         }
      }  ;  // end of get column alternate info ...

}  ;  // end of DbColumnInfo

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Describe / define the DbFastLoad

class DbFastLoad
{
   private:

   public:

      long              lRowCount;           // number of rows loaded
      long              lRowCommited;        // number of rows commited

      ULONG             ulRowLen;            // row length
      HACCESSOR         hAccessor;           // data accessor
      HRESULT           hrLast;              // last HRESULT

      // connection and fields info (don't ever release these resources here)

      DbConnection   *pConnection;         // pointer to connection object
      DbFields       *pFields;             // pointer to fields
      _CHAR            *pRowValues;          // row values

      // the following members need to be release at some point

      DBBINDING        *pDbBindings;         // pointer to field bindings
      DBBINDSTATUS     *pDbBindStatus;       // pointer to column status bind.
      IDBCreateSession *pIDbCreateSession;   // pointer to session
      IOpenRowset      *pIOpenRowsetFL;      // pointer to row set
      IRowsetFastLoad  *pIFastLoad;          // pointer to fast load interface
      IAccessor        *pIAccessor;          // pointer to accessor object

      // init the 'FlDBFastLoad' object

      DbFastLoad()
      {
         lRowCount         = 0;
         lRowCommited      = 0;

         hAccessor         = 0;
         ulRowLen          = 0;

         pRowValues        = NULL;
         pDbBindings       = NULL;
         pDbBindStatus     = NULL;
         pConnection       = NULL;
         pIDbCreateSession = NULL;
         pIOpenRowsetFL    = NULL;
         pIFastLoad        = NULL;
         pIAccessor        = NULL;
      }  // end of constructure

      // insert row

      inline bool InsertRow() {
         hrLast = pIFastLoad->InsertRow(hAccessor,pRowValues);
         lRowCount++;
         return(hrLast == 0);
      }  // end of insert row

      // commit all inserted rows ...

      bool Commit() {
         hrLast = pIFastLoad->Commit(TRUE);
         lRowCommited = lRowCount;
         return(hrLast == 0);
      }  // end of commit data

      // get field pointer

      void *GetFieldPointer(unsigned int indx) {
         void *pData = NULL;
         if (pDbBindings && pFields) {
            if ((indx >= 0) && (indx < pFields->iCount)) {
               pData = (void*)(pRowValues+pDbBindings[indx].obValue);
            }
         }
         return(pData);
      }  // end of get field pointer

      // release allocated resources

     ~DbFastLoad() {

         // free data buffer

         if (pRowValues)
            delete pRowValues;

         // release binding related resources

         if (pDbBindStatus)
            delete pDbBindStatus;
         if (pDbBindings)
            delete pDbBindings;

         // release accessor related resources

         if (pIAccessor && hAccessor)
            pIAccessor->ReleaseAccessor(hAccessor, NULL);
         if (pIAccessor)
            pIAccessor->Release();

         // release rowset and session releated resources

         if (pIFastLoad)
            pIFastLoad->Release();
         if (pIOpenRowsetFL)
            pIOpenRowsetFL->Release();
         if (pIDbCreateSession)
            pIDbCreateSession->Release();

         // reset the "fast-load" property on the connection

         if (pConnection)
            pConnection->SetFastLoadProperty(false);
      }  // end of destructore (release allocated resources)

}  ;  // end of DbFastLoad define the fast load class

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Describe / define the DbResult

class DbResult
{
   private:

      // column bindings ...

      DBBINDSTATUS   *pDbBindStatus;  // binding status

      DbConnection   *pParentObj;     // parent object that created this
      ICommandText   *pICommandText;  // pointer to command text

      // result columns description ...

      OLECHAR        *pColumnStrings; // result strings

      // fetched row info

      ULONG           cRowsRequested; // rows requested
      ULONG           cCurrentRow;    // current row
      HROW           *prghRows;       // row handles
      ULONG           ulRowLen;       // calculated row length

      // Pointer to the accessor
      //
      // OLE DB Accessors
      // An accessor contains the binding information for one or more columns
      // or parameters. The bindings supplied by the caller describe a data
      // structure with offsets for each value. The consumer is responsible for
      // correctly calculating the offsets. The consumer specifies the desired
      // data type within the structure and the provider agrees to the binding
      // if the conversion is legal and supported. The accessors are created on
      // the rowset or command and their implementation is specific to the
      // provider. They contain information or code to pack and unpack rows
      // held by the provider. The consumer uses them like handles on the
      // access functions of the row or column. Consumers can use more than
      // one accessor, or a single accessor can define the mapping between a
      // structure and multiple columns or parameters.
      //
      // Accessors in OLE DB are similar to descriptor handles in ODBC. Each
      // represents a reusable, extensible set of binding information for
      // multiple columns of a result set or parameters to a statement. Multiple
      // accessors can be created for a single rowset, just as multiple
      // descriptors can be used on a single result set. Accessors can be
      // created on a command object so that they can be shared between multiple
      // rowsets the same way that descriptors can be shared between multiple
      // result sets (hStmts).

      IAccessor      *pIAccessor;     // Pointer to the accessor
      HACCESSOR       hAccessor;      // accessor handle

      void ResetResources();

   public:

      // fetched data interfaces ...

      IRowset          *pIRowset;         // pointer to the row set

      IMultipleResults *pIMultipleResults; // pointer to multiple results
      EDbResults        eLastResultStatus; // last result enumerator

      // needed for fetching values ...
      // we need this variables here so we can implement inline methods that
      // will allow the user to fetch the fields / column values with ease ...

      DbFields       *pFields;        // consumer fields info ...
      DbParams       *pParams;        // query parameters (if any)

      DBBINDING      *pDbBindings;    // bindings information
      DBCOLUMNINFO   *pColumnsInfo;   // columns information
      _CHAR          *pRowValues;     // row values ...
      ULONG           cRowsObtained;  // Count of rows obtained
      ULONG           nCols;          // number of columns in the result

      HRESULT         hrLastError;    // contains the last HRESULT

      // affected number of rows ...

      DBROWCOUNT lAffectedRows;

      // declare / define 

      DbResult();
     ~DbResult();

      inline void Set(DbFields *pFList) { pFields = pFList; }

      void SetParent(DbConnection *pPObj) { pParentObj = pPObj; }
      void SetCommandText(ICommandText *pCommText)
              { pICommandText = pCommText; }
      void SetRowset(IRowset *pRSet);

      bool GetColumnsInfo();
      bool CreateBindings();
      bool CreateBindings(DbFields *pFields);
      bool GetAccessor();

      bool Execute();

      bool ExecuteNoneQuery() {
         eLastResultStatus = EDbNoResult;
         hrLastError = pICommandText->Execute(NULL, IID_NULL,
            pParams ? pParams->pParams : NULL,
            &lAffectedRows, (IUnknown**) NULL);

         if (hrLastError != S_OK) {
            // display error if needed/possible
            pParentObj->CheckError(hrLastError);
         }

         return(hrLastError == S_OK);
      }  // end of execute without expecting a row set/s back

      bool Open(DbFields *pFields,int iNumRows);
      inline bool Open() { return(Open(NULL,1)); }
      inline bool Open(DbFields *pFields) { return(Open(pFields,1)); }

      inline bool GotRows()   {
         return(pIRowset != NULL) && (eLastResultStatus == EDbOk); }
      inline bool GotParams() {
         return(pIRowset == NULL) && (eLastResultStatus == EDbOk); }

      bool Next(DbFields *pFields);
      inline bool Next() { return(Next(NULL)); }

      bool First() { return(Next()); }
      void Close();
      void Free();

      inline bool Eof() {
         return(cRowsObtained == 0);
      }

      // -----------------------------------------------------------------------
      // Function : inline EDbResults GetResult()
      // Purpose  : fetch results ...
      //          : you will call this 'GetResult()' function after calling
      //          : 'ClResults::Execute'. Use the inline function 'GotRows()'
      //          : to see if we got a row set or 'GotParams()' to see if we
      //          : got parameters ...
      // Date     : Feb/2k1  (ESob)
      // Updated  : Oct/2k1  (ESob) Updated to handle multiple result sets +
      //          :                 parameter return.

      inline bool GetResult()
      {
         if (!pIMultipleResults) {
            eLastResultStatus = EDbNoResult;
            return(false);
         }

         if (pIRowset) {
            pIRowset->Release();
            pIRowset = NULL;
         }

         hrLastError = pIMultipleResults->GetResult(
            NULL,
            0,
            IID_IRowset,
            &lAffectedRows,
            (IUnknown**) &pIRowset);

         if ((hrLastError == S_OK) && (!pIRowset)) // && (lAffectedRows > 0))
            pParams->GetValues();

         if (hrLastError == DB_S_NORESULT) {
            pIMultipleResults->Release();
            pIMultipleResults = NULL;
         }

         eLastResultStatus = (EDbResults)hrLastError;
         return(eLastResultStatus == S_OK);
      }  ;

      // -----------------------------------------------------------------------
      // provide to fetch numbers ...

      inline int AsInteger(ULONG iColNum)
      {
         if (IsValidColumn(iColNum))
         {
            if (pDbBindings[iColNum].wType == DBTYPE_I2)
            {
               DBCOLUMNINFO *pColInfo =
                  pColumnsInfo ? &pColumnsInfo[iColNum] : NULL;
               DbColumnInfo cColInfo(pRowValues,
                  &pDbBindings[iColNum],pColInfo);

               int result;
               memcpy_s((char*)&result,sizeof(int),
                        (char*)cColInfo.pVal,sizeof(int));
               return((int)result);
            }
         }
         // TODO : through an exception here
         return(0);
      }  // end of return integer ...

      inline int AsLong(ULONG iColNum)
      {
         if (IsValidColumn(iColNum))
         {
            if (pDbBindings[iColNum].wType == DBTYPE_I4)
            {
               DBCOLUMNINFO *pColInfo =
                  pColumnsInfo ? &pColumnsInfo[iColNum] : NULL;
               DbColumnInfo cColInfo(pRowValues,
                  &pDbBindings[iColNum],pColInfo);

               long result;
               memcpy_s((char*)&result, sizeof(long),
                        (char*)cColInfo.pVal, sizeof(long));
               return((long)result);
            }
         }
         // TODO : through an exception here
         return(0);
      }  // end of return long ...

      inline double AsDouble(ULONG iColNum) {
         if (IsValidColumn(iColNum)) {
            if (pDbBindings[iColNum].wType == DBTYPE_R8) {
               DBCOLUMNINFO *pColInfo =
                  pColumnsInfo ? &pColumnsInfo[iColNum] : NULL;
               DbColumnInfo cColInfo(pRowValues,
                  &pDbBindings[iColNum],pColInfo);

               double result;
               memcpy_s((char*)&result, sizeof(double),
                        (char*)cColInfo.pVal, sizeof(double));
               return((double)result);
            }
         }
         // TODO : through an exception here
         return(0);
      }  // end of return double ...

      // provide to fetch datetime ...

      inline bool GetDateTime(TDbDatetime *dtDTime,ULONG iColNum)
      {
         bool isok = false;

         if (IsValidColumn(iColNum)) {
            if (pDbBindings[iColNum].wType == DBTYPE_DBTIMESTAMP) {
               DBCOLUMNINFO *pColInfo =
                  pColumnsInfo ? &pColumnsInfo[iColNum] : NULL;
               DbColumnInfo cColInfo(pRowValues,
                  &pDbBindings[iColNum],pColInfo);

               memcpy_s((char*)dtDTime, sizeof(DBTIMESTAMP),
                        (char*)cColInfo.pVal, sizeof(DBTIMESTAMP));

               // make sure that we have a good datetime ...

               if ((dtDTime->month  < 1) || (dtDTime->month > 12))
                  dtDTime->month  = 0;
               if ((dtDTime->day    < 1) || (dtDTime->day   > 31))
                  dtDTime->day    = 0;
               if ((dtDTime->year   < 0) || (dtDTime->year  > 6000))
                  dtDTime->year   = 0;
               if ((dtDTime->hour   < 0) || (dtDTime->hour  > 24))
                  dtDTime->hour   = 0;
               if ((dtDTime->minute < 0) || (dtDTime->hour  > 60))
                  dtDTime->minute = 0;
               if ((dtDTime->second < 0) || (dtDTime->second> 60))
                  dtDTime->second = 0;

               isok = true;
            }
         }

         // TODO : through an exception here if something is wrong ...

         if (!isok) {
            dtDTime->month    = 0;
            dtDTime->day      = 0;
            dtDTime->year     = 0;
            dtDTime->hour     = 0;
            dtDTime->minute   = 0;
            dtDTime->second   = 0;
            dtDTime->fraction = 0;
         }

         return(isok);
      }  // end of get datetime field ...

      // read blob as a text string of chars ...
      inline bool CopyBLOBToString(TCHAR *pOutStr,int iMaxStrLen,ULONG iColNum)
      {
         bool isok = false;
         *pOutStr = NULL;
         if (IsValidColumn(iColNum))
         {

            #ifdef DB_TEXT_AS_BYTE
               if (pDbBindings[iColNum].wType == DBTYPE_BYTES) {
                  DBCOLUMNINFO *pColInfo =
                     pColumnsInfo ? &pColumnsInfo[iColNum] : NULL;
                  DbColumnInfo cColInfo(pRowValues,
                     &pDbBindings[iColNum],pColInfo);

                  if (cColInfo.bSuccess && (!cColInfo.bIsNull)) {
                     int slen = pDbBindings[iColNum].cbMaxLen;

                     if (slen > iMaxStrLen)
                        slen = iMaxStrLen;

                     memcpy_s(pOutStr,slen,(char*)cColInfo.pVal,slen);
                     pOutStr[slen] = NULL;
                  }
               }
            #else
               if (pDbBindings[iColNum].wType == DBTYPE_IUNKNOWN) {
                  DBCOLUMNINFO *pColInfo =
                     pColumnsInfo ? &pColumnsInfo[iColNum] : NULL;
                  DbColumnInfo cColInfo(pRowValues,
                     &pDbBindings[iColNum],pColInfo);

                  if (cColInfo.bSuccess && (!cColInfo.bIsNull)) {
                     ULONG ulReadBytes;
                     (*((ISequentialStream**)cColInfo.pVal))->
                        Read(pOutStr,iMaxStrLen,&ulReadBytes);
                     pOutStr[ulReadBytes] = NULL;
                  }
               }
            #endif

         }
         return(isok);
      }  // end of copy BLOB object to given string buffer ...

      // copy string into given buffer ...
      inline bool CopyString(TCHAR *pOutStr,int iMaxStrLen,ULONG iColNum)
      {
         bool isok = false;
         *pOutStr = NULL;
         if (IsValidColumn(iColNum))
         {
            DBCOLUMNINFO *pColInfo =
               pColumnsInfo ? &pColumnsInfo[iColNum] : NULL;
            DbColumnInfo cColInfo(pRowValues,
               &pDbBindings[iColNum],pColInfo);

            // 06-11-04 added UNICODE string handling...
            if ((pDbBindings[iColNum].wType == DBTYPE_STR) ||
                (pDbBindings[iColNum].wType == DBTYPE_WSTR))
            {
               if (cColInfo.bSuccess)
               {
                  int slen = pDbBindings[iColNum].cbMaxLen;

                  if (slen > iMaxStrLen)
                     slen = iMaxStrLen;

                  TCHAR *buff = (TCHAR*)cColInfo.pVal;
                  _CharCopyString(pOutStr,slen,buff,slen);
               }
            }
            else
            if (pDbBindings[iColNum].wType == DBTYPE_BSTR)
            {
               if (cColInfo.bSuccess) {
                  int slen = pDbBindings[iColNum].cbMaxLen;

                  if (slen > iMaxStrLen)
                     slen = iMaxStrLen;

                  Kif::Win32::String::Copy(
                     pOutStr,iMaxStrLen,(TCHAR*)cColInfo.pVal);
               }
            }
         }
         else
            *pOutStr = NULL;

         return(isok);
      }  // end of return pointer to string ...

}  ;  // end of DbResult

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Describe / define the DbCommonQueries class

class DbCommonQueries
{
   public:

   static long GetTableRecordCount(DbSettings &Settings, TCHAR *pTableName);
   static long GetScalar(DbConnection &Connection, TCHAR *pQuery);

}  ;  // end of common query requests

#pragma endregion
// -----------------------------------------------------------------------------

}  // end of Kif::Win32::Data::OleDbClient
}  // end of Kif::Win32::Data
}  // end of Kif::Win32
}  // end of Kif


